package com.fhzn.boss.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fhzn.boss.entity.App;
import com.fhzn.boss.enums.Constants;
import com.fhzn.boss.enums.WebResponseEnum;
import com.fhzn.boss.mapper.*;
import com.fhzn.boss.service.AppService;
import com.fhzn.boss.service.OperatorLogService;
import com.fhzn.boss.web.converter.AppConverter;
import com.fhzn.boss.web.request.AppOpRequest;
import com.fhzn.boss.web.request.AppQueryRequest;
import com.fhzn.boss.web.vo.*;
import com.fhzn.commons.toolkit.exception.BuzException;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author 老顾
 * @apiNote
 * @since 2024/1/3
 */
@Service
@RequiredArgsConstructor
public class AppServiceImpl implements AppService {

    private final AppMapper appMapper;
    private final ResourceMapper resourceMapper;
    private final TagMapper tagMapper;
    private final UserMapper userMapper;
    private final LabelMapper labelMapper;
    private final OperatorLogService operatorLogService;

    @Override
    public Page<App> appWithPage(AppQueryRequest request) {
        request.checkParams();
        request.rewriteParams();
        request.getOffset();
        return appMapper.getAppsWithPage(request);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AppVO add(AppOpRequest request) {
        request.checkAdd();

        App app = request.genServiceParam();
        try {
            appMapper.insert(app);
        } catch (Exception e) {
            throw new BuzException("新建应用失败，应用信息重复！");
        }
        operatorLogService.operatorLog(request.getHandler(), Constants.APPLICATION_SECTION, Constants.APPLICATION_ADD_METHOD, request.getId(), null, app, Constants.APP_CODE);
        return AppConverter.fromApp(app);
    }

    @Override
    public AppVO queryOne(AppQueryRequest request) {
        request.checkId();
        return AppConverter.fromApp(appMapper.selectById(request.getId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String update(AppOpRequest request) {
        request.checkUpdate();
        App app = appMapper.selectById(request.getId());
        app.setModifier(request.getModifier());
        try {
            appMapper.updateById(request.genServiceParam());
        } catch (Exception e) {
            throw new BuzException("编辑应用信息失败，应用信息重复！");
        }
        operatorLogService.operatorLog(request.getHandler(), Constants.APPLICATION_SECTION, Constants.APPLICATION_MESSAGE_UPDATE, request.getId(), app, appMapper.selectById(request.getId()), Constants.APP_CODE);
        return WebResponseEnum.SUCCESS.getMessage();
    }


    @Override
    public ResponseEntity<byte[]> export(String code) throws IOException {
        AppExportVO applicationsData = appMapper.findByCode(code);
        List<ResourceExportVO> resourcesData = resourceMapper.findByAppCode(code);
        List<TagExportVO> tagsData = tagMapper.findByAppCode(code);
        List<UserExportVO> usersData = userMapper.findUsersByAppCode(code);
        List<LabelExportVO> labelsData = labelMapper.findLabelsByAppCode(code);
        ObjectMapper objectMapper = new ObjectMapper();
        // 格式化JSON输出
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        byte[] applicationBytes = convertObjectToJsonBytes(applicationsData, objectMapper);
        byte[] resourcesBytes = convertListToJsonBytes(resourcesData, objectMapper);
        byte[] tagsBytes = convertListToJsonBytes(tagsData, objectMapper);
        byte[] usersBytes = convertListToJsonBytes(usersData, objectMapper);
        byte[] labelsBytes = convertListToJsonBytes(labelsData, objectMapper);
        byte[] zipFileBytes = createZipFile(applicationBytes, resourcesBytes, tagsBytes, usersBytes, labelsBytes);

        // 设置所需的文件名
        String fileName = code + " 应用配置导出.zip";
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
        fileName = fileName.replace("+", " ");

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", fileName);

        return new ResponseEntity<>(zipFileBytes, headers, HttpStatus.OK);
    }

    private byte[] convertObjectToJsonBytes(Object data, ObjectMapper objectMapper) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        objectMapper.writeValue(baos, data);
        baos.flush();
        return baos.toByteArray();
    }

    private byte[] convertListToJsonBytes(List<?> data, ObjectMapper objectMapper) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        objectMapper.writeValue(baos, data);
        baos.flush();
        return baos.toByteArray();
    }

    private byte[] createZipFile(byte[] applicationBytes, byte[] resourcesBytes, byte[] tagsBytes, byte[] usersBytes, byte[] labelsBytes) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) {
            // 添加JSON文件到ZIP
            addFileToZip("application.json", applicationBytes, zipOutputStream);
            addFileToZip("resource.json", resourcesBytes, zipOutputStream);
            addFileToZip("tag.json", tagsBytes, zipOutputStream);
            addFileToZip("user.json", usersBytes, zipOutputStream);
            addFileToZip("label.json", labelsBytes, zipOutputStream);
        }
        return baos.toByteArray();
    }

    private void addFileToZip(String fileName, byte[] fileData, ZipOutputStream zipOutputStream) throws IOException {
        ZipEntry zipEntry = new ZipEntry(fileName);
        zipOutputStream.putNextEntry(zipEntry);
        zipOutputStream.write(fileData);
        zipOutputStream.closeEntry();
    }
}
